#include "bsp.h"

static uint32_t *ptrd;
static uint32_t address = 0x00000000U;
static uint32_t data0   = 0x01234567U;

/* calculate the number of page to be programmed/erased */
static uint32_t PageNum = (FMC_WRITE_END_ADDR - FMC_WRITE_START_ADDR) / FMC_PAGE_SIZE;
/* calculate the number of page to be programmed/erased */
static uint32_t WordNum = ((FMC_WRITE_END_ADDR - FMC_WRITE_START_ADDR) >> 2);

/*!
    \brief      erase fmc pages from FMC_WRITE_START_ADDR to FMC_WRITE_END_ADDR
    \param[in]  none
    \param[out] none
    \retval     none
*/
void fmc_erase_pages(void)
{
    uint32_t EraseCounter;

    /* unlock the flash program/erase controller */
    fmc_unlock();

    /* clear all pending flags */
    fmc_flag_clear(FMC_FLAG_END | FMC_FLAG_WPERR | FMC_FLAG_PGAERR | FMC_FLAG_PGERR);

    /* erase the flash pages */
    for(EraseCounter = 0; EraseCounter < PageNum; EraseCounter++){
        fmc_page_erase(FMC_WRITE_START_ADDR + (FMC_PAGE_SIZE * EraseCounter));
        fmc_flag_clear(FMC_FLAG_END | FMC_FLAG_WPERR | FMC_FLAG_PGAERR | FMC_FLAG_PGERR);
    }

    /* lock the main FMC after the erase operation */
    fmc_lock();
}

/*!
    \brief      program fmc word by word from FMC_WRITE_START_ADDR to FMC_WRITE_END_ADDR
    \param[in]  none
    \param[out] none
    \retval     none
*/
void fmc_program(void)
{
    /* unlock the flash program/erase controller */
    fmc_unlock();

    address = FMC_WRITE_START_ADDR;

    /* program flash */
    while(address < FMC_WRITE_END_ADDR){
        fmc_word_program(address, data0);
        address += 4;
        fmc_flag_clear(FMC_FLAG_END);
        fmc_flag_clear(FMC_FLAG_WPERR);
        fmc_flag_clear(FMC_FLAG_PGERR);
    }

    /* lock the main FMC after the program operation */
    fmc_lock();
}

/*!
    \brief      check fmc erase result
    \param[in]  none
    \param[out] none
    \retval     none
*/
void fmc_erase_pages_check(void)
{
    uint32_t i;

    ptrd = (uint32_t *)FMC_WRITE_START_ADDR;

    /* check flash whether has been erased */
    for(i = 0; i < WordNum; i++){
        if(0xFFFFFFFF != (*ptrd)){
            UART_PRINTF("fmc_erase_pages_check error! \r\n");
            break;
        }else{
            ptrd++;
        }
    }
}

/*!
    \brief      check fmc program result
    \param[in]  none
    \param[out] none
    \retval     none
*/
void fmc_program_check(void)
{
    uint32_t i;

    ptrd = (uint32_t *)FMC_WRITE_START_ADDR;

    /* check flash whether has been programmed */
    for(i = 0; i < WordNum; i++){
        if((*ptrd) != data0){
            UART_PRINTF("fmc_program_check error! \r\n");
            break;
        }else{
            ptrd++;
        }
    }
}


void FlashWrite(uint16_t len,uint8_t  *data,uint32_t addr_start)
{	
	uint16_t i=0;
	uint32_t temp=0;
	uint32_t addr=addr_start;
	
	fmc_state_enum  fmc_state=FMC_READY;
	fmc_unlock();

	for(i=0;i<len/4;i++) 
	{		
		temp = (data[0]<<0)+(data[1]<<8)+(data[2]<<16)+(data[3]<<24);

		fmc_state=fmc_word_program(addr, temp);
		
		if(fmc_state!=FMC_READY)
		{
			return;
		}
		
		fmc_flag_clear(FMC_FLAG_END);
		fmc_flag_clear(FMC_FLAG_WPERR);
		fmc_flag_clear(FMC_FLAG_PGERR);
		
		data += 4;
		addr += 4;
	}
	
	if((len % 4)==3)
	{
		temp = (data[0]<<0)+(data[1]<<8)+(data[2]<<16);
		temp = temp | 0xff000000;
		fmc_state=fmc_word_program(addr,temp);
		fmc_flag_clear(FMC_FLAG_END);
		fmc_flag_clear(FMC_FLAG_WPERR);
		fmc_flag_clear(FMC_FLAG_PGERR);
	}
	else
	{
		if((len % 4)==2)
		{
			temp = (data[0]<<0)+(data[1]<<8);
			temp = temp | 0xffff0000;
		  fmc_state=fmc_word_program(addr,temp);
			fmc_flag_clear(FMC_FLAG_END);
			fmc_flag_clear(FMC_FLAG_WPERR);
			fmc_flag_clear(FMC_FLAG_PGERR);
		}
		else
		{
			if((len % 4)==1)
			{
				temp = (data[0]<<0);
				temp = temp |  0xffffff00 ;
				fmc_state=fmc_word_program(addr,temp);
				fmc_flag_clear(FMC_FLAG_END);
				fmc_flag_clear(FMC_FLAG_WPERR);
				fmc_flag_clear(FMC_FLAG_PGERR);
			}
		}
	}
	fmc_lock();
}


void FlashRead(uint16_t len,uint8_t *outdata,uint32_t addr_start)
{	
	uint32_t addr;
	uint16_t i;
	
	addr = addr_start;
	
	for(i=0;i<len;i++) 
	{
		*outdata = *(uint8_t*) addr;
		addr = addr + 1;
		outdata++;
	}
}


void FlashErase(uint32_t start, uint32_t end)
{
	uint32_t EraseCounter;
	
	fmc_state_enum fmc_state=FMC_READY;

	  /* unlock the flash program/erase controller */
    fmc_unlock();

    /* clear all pending flags */
		fmc_flag_clear(FMC_FLAG_END);
		fmc_flag_clear(FMC_FLAG_WPERR);
		fmc_flag_clear(FMC_FLAG_PGERR);

    /* erase the flash pages */
    while(start < end)
	{
		EraseCounter = start/FLASH_PAGE_SIZE;
        fmc_state=fmc_page_erase(EraseCounter*FLASH_PAGE_SIZE);
		if(fmc_state!=FMC_READY)
		{
			return;
		}
		
		fmc_flag_clear(FMC_FLAG_END);
		fmc_flag_clear(FMC_FLAG_WPERR);
		fmc_flag_clear(FMC_FLAG_PGERR);

		start += FLASH_PAGE_SIZE;
    }

    /* lock the main FMC after the erase operation */
    fmc_lock();
}

void FlashTest(void)
{
	uint8 i;
	
	uint8 WriteData[TEST_DATA_LEN]={0x12,0x34,0x56,0x78,0x90,0xab,0xcd,0xef,0xcc,0xcc};
	uint8 ReadData[TEST_DATA_LEN];
	
	FlashErase(FMC_WRITE_START_ADDR, FMC_WRITE_START_ADDR+FLASH_PAGE_SIZE);
	FlashWrite(TEST_DATA_LEN,WriteData,FMC_WRITE_START_ADDR);
	FlashRead(TEST_DATA_LEN,ReadData, FMC_WRITE_START_ADDR);
	
	UART_PRINTF("Write Data[");
	for(i=0;i<TEST_DATA_LEN;i++)
	{
		UART_PRINTF(" 0x%x",WriteData[i]);
	}
	UART_PRINTF("]\r\n");

	UART_PRINTF("Read Data[");
	for(i=0;i<TEST_DATA_LEN;i++)
	{
		UART_PRINTF(" 0x%x",ReadData[i]);
	}
	UART_PRINTF("]\r\n");
}


